package com.github.shuaidd.core;


import org.activiti.content.storage.api.ContentStorage;
import org.activiti.content.storage.fs.FileSystemContentStorage;
import org.activiti.engine.*;
import org.activiti.engine.impl.asyncexecutor.AsyncExecutor;
import org.activiti.engine.impl.asyncexecutor.DefaultAsyncJobExecutor;
import org.activiti.engine.impl.cfg.ProcessEngineConfigurationImpl;
import org.activiti.engine.parse.BpmnParseHandler;
import org.activiti.engine.runtime.Clock;
import org.activiti.form.api.FormRepositoryService;
import org.activiti.form.engine.FormEngineConfiguration;
import org.activiti.form.engine.configurator.FormEngineConfigurator;
import org.activiti.spring.ProcessEngineFactoryBean;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.DependsOn;
import org.springframework.core.env.Environment;
import org.springframework.transaction.PlatformTransactionManager;

import javax.sql.DataSource;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;

/**
 * 描述 aitiviti 6.0.0 配置信息
 *
 * @author ddshuai
 * @date 2020-06-16 21:36
 **/
@Configuration
public class ActivitiEngineConfiguration {
    private final Logger logger = LoggerFactory.getLogger(ActivitiEngineConfiguration.class);

    private final DataSource dataSource;
    private final PlatformTransactionManager transactionManager;
    private final Environment environment;

    private static final String PROP_FS_ROOT = "contentstorage.fs.rootFolder";
    private static final String PROP_FS_DEPTH = "contentstorage.fs.depth";
    private static final String PROP_FS_BLOCK_SIZE = "contentstorage.fs.blockSize";
    private static final String PROP_FS_CREATE_ROOT = "contentstorage.fs.createRoot";

    private static final Integer DEFAULT_FS_DEPTH = 4;
    private static final Integer DEFAULT_FS_BLOCK_SIZE = 1024;

    public ActivitiEngineConfiguration(DataSource dataSource, PlatformTransactionManager transactionManager, Environment environment) {
        this.dataSource = dataSource;
        this.transactionManager = transactionManager;
        this.environment = environment;
    }

    @Bean(name="processEngine")
    public ProcessEngineFactoryBean processEngineFactoryBean() {
        ProcessEngineFactoryBean factoryBean = new ProcessEngineFactoryBean();
        factoryBean.setProcessEngineConfiguration(processEngineConfiguration());
        return factoryBean;
    }

    public ProcessEngine processEngine() {
        // Safe to call the getObject() on the @Bean annotated processEngineFactoryBean(), will be
        // the fully initialized object instanced from the factory and will NOT be created more than once
        try {
            return processEngineFactoryBean().getObject();
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    @Bean(name="processEngineConfiguration")
    public ProcessEngineConfigurationImpl processEngineConfiguration() {
        CustomProcessEngineConfiguration processEngineConfiguration = new CustomProcessEngineConfiguration();
        processEngineConfiguration.setDataSource(dataSource);
        processEngineConfiguration.setDatabaseSchemaUpdate(ProcessEngineConfiguration.DB_SCHEMA_UPDATE_TRUE);
        processEngineConfiguration.setTransactionManager(transactionManager);
        processEngineConfiguration.setAsyncExecutorActivate(true);
        processEngineConfiguration.setAsyncExecutor(asyncExecutor());

        // Limit process definition cache
        processEngineConfiguration.setProcessDefinitionCacheLimit(environment.getProperty("activiti.process-definitions.cache.max", Integer.class, 128));

        // Enable safe XML. See http://activiti.org/userguide/index.html#advanced.safe.bpmn.xml
        processEngineConfiguration.setEnableSafeBpmnXml(true);

        List<BpmnParseHandler> preParseHandlers = new ArrayList<BpmnParseHandler>();
        processEngineConfiguration.setPreBpmnParseHandlers(preParseHandlers);

        FormEngineConfiguration formEngineConfiguration = new FormEngineConfiguration();
        formEngineConfiguration.setDataSource(dataSource);

        FormEngineConfigurator formEngineConfigurator = new FormEngineConfigurator();
        formEngineConfigurator.setFormEngineConfiguration(formEngineConfiguration);
        processEngineConfiguration.addConfigurator(formEngineConfigurator);

        /*
         * 添加客户端的自定义mapper
         */
        HashSet<String> mappers = buildCustomMappers();
        processEngineConfiguration.setCustomMybatisXMLMappers(mappers);

        return processEngineConfiguration;
    }

    private HashSet<String> buildCustomMappers() {
        HashSet<String> mappers = new HashSet<>();
        SpringBootVFS vfs = new SpringBootVFS();
        try {
            List<String> paths = vfs.list(null,"/mapper");
            mappers.addAll(paths);
        } catch (IOException e) {
            logger.error("加载自定义mapper异常");
        }
        return mappers;
    }

    @Bean
    public AsyncExecutor asyncExecutor() {
        DefaultAsyncJobExecutor asyncExecutor = new DefaultAsyncJobExecutor();
        asyncExecutor.setDefaultAsyncJobAcquireWaitTimeInMillis(5000);
        asyncExecutor.setDefaultTimerJobAcquireWaitTimeInMillis(5000);
        return asyncExecutor;
    }

    @Bean(name="clock")
    @DependsOn("processEngine")
    public Clock getClock() {
        return processEngineConfiguration().getClock();
    }

    @Bean
    public RepositoryService repositoryService() {
        return processEngine().getRepositoryService();
    }

    @Bean
    public RuntimeService runtimeService() {
        return processEngine().getRuntimeService();
    }

    @Bean
    public TaskService taskService() {
        return processEngine().getTaskService();
    }

    @Bean
    public HistoryService historyService() {
        return processEngine().getHistoryService();
    }

    @Bean
    public FormService formService() {
        return processEngine().getFormService();
    }

    @Bean
    public IdentityService identityService() {
        return processEngine().getIdentityService();
    }

    @Bean
    public ManagementService managementService() {
        return processEngine().getManagementService();
    }

    @Bean
    public FormRepositoryService formEngineRepositoryService() {
        return processEngine().getFormEngineRepositoryService();
    }

    @Bean
    public org.activiti.form.api.FormService formEngineFormService() {
        return processEngine().getFormEngineFormService();
    }

    @Bean
    public ContentStorage contentStorage() {

        // TODO: separate the configuration of each instance to a class, so this class doesn't depend on any IMPL being
        // on the classpath

        String fsRoot = environment.getProperty(PROP_FS_ROOT);
        logger.info("Using file-system based content storage (" + fsRoot + ")");
        Integer iterationDepth = environment.getProperty(PROP_FS_DEPTH, Integer.class, DEFAULT_FS_DEPTH);
        Integer blockSize = environment.getProperty(PROP_FS_BLOCK_SIZE, Integer.class, DEFAULT_FS_BLOCK_SIZE);

        assert fsRoot != null;
        File root = new File(fsRoot);
        if(environment.getProperty(PROP_FS_CREATE_ROOT, Boolean.class, Boolean.FALSE) && !root.exists()) {
            logger.info("Creating content storage root and possible missing parents: " + root.getAbsolutePath());
            root.mkdirs();
        }
        if (root.exists()) {
            logger.info("File system root : " + root.getAbsolutePath());
        }
        return new FileSystemContentStorage(root, blockSize, iterationDepth);
    }
}
